package com.kizuki.thread;

import org.junit.Test;

import java.time.LocalDateTime;
import java.util.concurrent.*;

/**
 * 三大方法
 * 七大参数
 * 四种拒绝策略
 */
public class ThreadPool {

    /**
     * 四大方法，但是不推荐使用Executors，推荐使用底层的ThreadPoolExecutor
     * FixedThreadPool 和 SingleThreadPool 允许请求队列长度为 Integer.MAX_VALUE
     * CachedThreadPool 和 ScheduledThreadPool 允许创建线程数量为 Integer.MAX_VALUE
     */
    @Test
    public void testExecutors(){
//        ExecutorService threadPool = Executors.newSingleThreadExecutor(); // 单个线程
//        ExecutorService threadPool = Executors.newFixedThreadPool(5); // 固定5个线程
        ExecutorService threadPool = Executors.newCachedThreadPool(); // 可伸缩

        for (int i = 0; i < 6; i++) {
            threadPool.execute(()->{
                System.out.println(Thread.currentThread().getName() + "正在运行");
            });
        }
    }

    @Test
    public void testExecutors2() throws InterruptedException {
        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(3);
        /**
         * scheduleWithFixedDelay
         * 是以上一个任务结束时开始计时，period时间过去后，立即执行
         */
        threadPool.scheduleWithFixedDelay(
                ()-> System.out.println("scheduleWithFixedDelay 定时任务: " + LocalDateTime.now()),
                0,
                2,
                TimeUnit.SECONDS
        );

        /**
         * 是以上一个任务开始的时间计时，period时间过去后，检测上一个任务是否执行完毕，
         * 如果上一个任务执行完毕，则当前任务立即执行，如果上一个任务没有执行完毕，则需要等上一个任务执行完毕后立即执行。
         */
        threadPool.scheduleAtFixedRate(
                ()-> {
                    System.out.println("scheduleAtFixedRate 固定频率：" + LocalDateTime.now());
                    while(true){

                    }
                },
                0,
                2,
                TimeUnit.SECONDS
        );

        /**
         * 只执行一次
         */
        threadPool.schedule(
                ()-> System.out.println("schedule 只执行一次"),
                2,
                TimeUnit.SECONDS
        );

        Thread.sleep(10000);
    }

    @Test
    public void testThreadPoolExecutor(){  // 七大参数
        /**
         * int corePoolSize,     银行中一直开着的窗口
         * int maximumPoolSize,  银行最大窗口
         * long keepAliveTime,   (maximumPoolSize - corePoolSize) 个窗口没人调用就释放的时间
         * TimeUnit unit,        keepAliveTime的单位
         * BlockingQueue<Runnable> workQueue,   银行等候室的大小
         * ThreadFactory threadFactory,         线程工厂 默认不用管
         * RejectedExecutionHandler handler     拒绝策略
         * 因此这个线程池最大处理的线程数为银行中最大窗口+等候室的大小 即 maximumPoolSize + workQueue的大小
         */

        final int maximumPoolSize = 3;
        final int blockSize = 1;
        final int len = maximumPoolSize + blockSize;

        ExecutorService threadPool = new ThreadPoolExecutor(
                2, // 核心线程池大小，
                maximumPoolSize, // 最大核心线程池大小
                3, // 超时没人调用就会释放
                TimeUnit.SECONDS, // 超时单位
                new LinkedBlockingDeque<>(blockSize), // 阻塞队列
                Executors.defaultThreadFactory(), // 线程工厂 defaultThreadFactory就可以了
                // 四种拒绝策略
//                new ThreadPoolExecutor.AbortPolicy() // 银行满了，还有人进来抛出异常 当线程数大于len可能抛出异常
                new ThreadPoolExecutor.DiscardPolicy() // 队列满了丢弃任务
//                new ThreadPoolExecutor.CallerRunsPolicy() // 哪里来的回到那里去
//                new ThreadPoolExecutor.DiscardOldestPolicy() // 队列满了尝试和最早的进程竞争，不跑出异常
        );

        final int textLen = len + 1;
        System.out.println("想要开启总线程数：" + textLen);
        for (int i = 0; i < textLen; i++) {
            threadPool.execute(()->{
                System.out.println(Thread.currentThread().getName() + "正在运行");
            });
        }
    }

    @Test
    public void testPolicy() {  // 四种拒绝策略
        new ThreadPoolExecutor.AbortPolicy(); // 银行满了，还有人进来抛出异常 当线程数大于len可能抛出异常
        new ThreadPoolExecutor.DiscardPolicy(); // 队列满了丢弃任务
        new ThreadPoolExecutor.CallerRunsPolicy(); // 哪里来的回到那里去
        new ThreadPoolExecutor.DiscardOldestPolicy(); // 队列满了尝试和最早的进程竞争，不跑出异常
    }

    @Test
    public void test() {  // 最大线程数应该怎样定义
        /**
         * cpu密集型 取机器的核数
         * io密集型 如15个io线程 则设置为两倍即是30
         */
        final int maximumPoolSize = Runtime.getRuntime().availableProcessors(); // 获取当前的pc的核数
        System.out.println(maximumPoolSize);
    }



}
